package com.unlcn.ils.kas.service.map.BaiDuMap.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.unlcn.ils.kas.dao.model.order.BaiduException;
import com.unlcn.ils.kas.service.map.BaiDuMap.config.BaiDuMapCoreConfig;
import com.unlcn.ils.kas.service.map.BaiDuMap.config.BaiDuMapGeoConfig;
import com.unlcn.ils.kas.service.order.IBaiduExceptionService;
import com.unlcn.ils.kas.service.util.BaseException;
import com.unlcn.ils.kas.service.util.HttpClientUtil;
import com.unlcn.ils.kas.service.util.enums.BaiDuMapStatusEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Component
public class BaiDuMapGeocodeUtil {

    private static final Logger logger = LoggerFactory.getLogger(BaiDuMapGeocodeUtil.class);

    // 编码格式。发送编码格式统一用UTF-8
    private static String ENCODING = "UTF-8";

    // 地址字符串转GEO点
    private static String ADDRESS2GEO_URL = "https://api.map.baidu.com/geocoder/v2/?output=json&address=%s&ak=%s";
    // 经纬度转GEO点
    private static String GPS2GEOPoint_URL = "https://api.map.baidu.com/geocoder/v2/?output=json&location=%s&coordtype=bd09ll&ak=%s";
    // 连接超时时间
    private static int socketTimeout = 10000;

    // 静态注入的方式
    private static BaiDuMapCoreConfig baiDuMapCoreConfig;

    private static BaiDuMapGeoConfig baiDuMapGeoConfig;

    @Autowired
    public void setBaiDuMapCoreConfig(BaiDuMapCoreConfig baiDuMapCoreConfig) {
        BaiDuMapGeocodeUtil.baiDuMapCoreConfig = baiDuMapCoreConfig;
    }

    @Autowired
    public void setBaiDuMapGeoConfig(BaiDuMapGeoConfig baiDuMapGeoConfig) {
        BaiDuMapGeocodeUtil.baiDuMapGeoConfig = baiDuMapGeoConfig;
    }

    public static IBaiduExceptionService iBaiduExceptionService;

    @Autowired
    public void setiBaiduExceptionService(IBaiduExceptionService iBaiduExceptionService) {
        BaiDuMapGeocodeUtil.iBaiduExceptionService = iBaiduExceptionService;
    }

    /**
     * 获取地理编码--经纬度
     * 
     * @param address 待解析的地址。最多支持84个字节。可以输入三种样式的值，分别是：1、标准的结构化地址信息，如北京市海淀区上地十街十号
     *                【推荐，地址结构越完整，解析精度越高】 2、支持“*路与*路交叉口”描述方式，如北一环路和阜阳路的交叉路口
     *                第二种方式并不总是有返回结果，只有当地址库中存在该地址描述时才有返回。 lat 纬度值 lng 经度值
     * @return
     * @throws BaseException
     */
    public static Map<String, Double> addressToGPS(String address) throws BaseException {
        Map<String, Double> map = Maps.newHashMap();

        try {
            address = address.replaceAll("null", "");
            address = address.substring(0, Math.min(address.length(), 42));
            String addressEncode = URLEncoder.encode(address, ENCODING);
            logger.info("addressToGPS addressStr:{} encoded: {}", address, addressEncode);
            String url = baiDuMapGeoConfig.getAddressToGpsUrl();
            logger.info("addressToGPSURL is ..." + url);
            if (StringUtils.isEmpty(url)) {
                url = ADDRESS2GEO_URL;
            }
            url = String.format(url, addressEncode, baiDuMapCoreConfig.getApiKey());
            String strReturn = HttpClientUtil.get(url, null, socketTimeout);
            JSONObject jsonObject = JSON.parseObject(strReturn);
            int status = jsonObject.getInteger("status");
            String msg = BaiDuMapStatusEnum.getTest(status);
            if (status == 0) {
                JSONObject location = jsonObject.getJSONObject("result").getJSONObject("location");
                if (!Objects.isNull(location)) {
                    map.put("lng", location.getDouble("lng"));
                    map.put("lat", location.getDouble("lat"));
                }
            } else {
                throw new BaseException(msg);
            }
        } catch (Exception e) {
            logger.error("addressToGPS URL error: {}", e.getMessage());
            BaiduException baiduException = new BaiduException();
            baiduException.setAddress(address);
            baiduException.setGmtCreate(new Date());
            baiduException.setExceptionMsg(e.getMessage());
            iBaiduExceptionService.insert(baiduException);

        }

        return map;
    }

    public static Map<String, String> addressToGEOPoint(String address) throws BaseException {
        Map<String, String> map = Maps.newHashMap();

        try {
            address = address.replaceAll("null", "");
            address = address.substring(0, Math.min(address.length(), 42));
            String addressEncode = URLEncoder.encode(address, ENCODING);
            logger.info("addressToGPS addressStr:{} encoded: {}", address, addressEncode);
            String url = baiDuMapGeoConfig.getAddressToGpsUrl();
            logger.info("addressToGPSURL is ..." + url);
            if (StringUtils.isEmpty(url)) {
                url = ADDRESS2GEO_URL;
            }
            url = String.format(url, addressEncode, baiDuMapCoreConfig.getApiKey());
            String strReturn = HttpClientUtil.get(url, null, socketTimeout);
            JSONObject jsonObject = JSON.parseObject(strReturn);
            int status = jsonObject.getInteger("status");
            String msg = BaiDuMapStatusEnum.getTest(status);
            if (status == 0) {
                JSONObject location = jsonObject.getJSONObject("result").getJSONObject("location");
                if (!Objects.isNull(location)) {
                    map.put("lng", location.getString("lng"));
                    map.put("lat", location.getString("lat"));
                }
            } else {
                throw new BaseException(msg);
            }
            if (!StringUtils.isEmpty(map)) {
                url = StringUtils.isEmpty(baiDuMapGeoConfig.getGpsToGeoPointUrl()) ? GPS2GEOPoint_URL
                        : baiDuMapGeoConfig.getGpsToGeoPointUrl();
                url = String.format(url, map.get("lat") + "," + map.get("lng"), baiDuMapCoreConfig.getApiKey());
                strReturn = HttpClientUtil.get(url, null, socketTimeout);
                jsonObject = JSON.parseObject(strReturn);
                status = jsonObject.getInteger("status");
                msg = BaiDuMapStatusEnum.getTest(status);
                logger.debug("GPS2GEOPoint ..." + strReturn);
                if (status == 0) {
                    JSONObject addressComponent = jsonObject.getJSONObject("result").getJSONObject("addressComponent");
                    if (!Objects.isNull(addressComponent)) {
                        map.put("province", addressComponent.getString("province"));
                        map.put("city", addressComponent.getString("city"));
                        map.put("district", addressComponent.getString("district"));
                    }
                } else {
                    throw new BaseException(msg);
                }
            } else {
                throw new BaseException("GPS2GEOPoint ... 解析经纬度失败");
            }
        } catch (Exception e) {
            logger.error("addressToGEOPoint URL error: {}", e);
            BaiduException baiduException = new BaiduException();
            baiduException.setAddress(address);
            baiduException.setGmtCreate(new Date());
            baiduException.setExceptionMsg(e.getMessage());
            iBaiduExceptionService.insert(baiduException);

        }

        return map;
    }

    /**
     * 逆地理编码
     * 
     * @param location 根据经纬度坐标获取地址。支持批量，多组坐标间用|分隔，单次请求最多解析20组坐标。超过20组取前20组解析。批量解析需使用batch参数。批量解析仅召回行政区划数据。
     *                 表达式：lat<纬度>,lng<经度>
     * @param pois     是否召回传入坐标周边的poi，0为不召回，1为召回。当值为1时，默认显示周边1000米内的poi。
     * @return
     * @throws BaseException
     */
    public static String GPSToAddress(String location, int pois) throws BaseException {
        String strAddress = GPSToAddressBatch(location, false, pois);
        return strAddress;
    }

    /**
     * 逆地理编码
     * 
     * @param location location
     *                 根据经纬度坐标获取地址。支持批量，多组坐标间用|分隔，单次请求最多解析20组坐标。超过20组取前20组解析。批量解析需使用batch参数。批量解析仅召回行政区划数据。
     *                 表达式：lat<纬度>,lng<经度>
     * @param bathch   批量查询
     * @param pois     是否召回传入坐标周边的poi，0为不召回，1为召回。当值为1时，默认显示周边1000米内的poi。
     * @return
     * @throws BaseException
     */
    public static String GPSToAddressBatch(String location, Boolean bathch, int pois) throws BaseException {
        String strAddress = null;
        String url = String.format(baiDuMapGeoConfig.getGpsToAddressUrl(), location, bathch, pois,
                baiDuMapCoreConfig.getApiKey());
        try {
            String strReturn = HttpClientUtil.get(url, null, socketTimeout);
            JSONObject jsonObject = JSON.parseObject(strReturn);
            int status = jsonObject.getInteger("status");
            String msg = BaiDuMapStatusEnum.getTest(status);
            if (status == 0) {
                strAddress = jsonObject.getJSONObject("result").getString("formatted_address");
            } else {
                throw new BaseException(msg);
            }
        } catch (Exception e) {
            logger.error("addressToGPS URL error: {}", e.getMessage());
            BaiduException baiduException = new BaiduException();
            baiduException.setAddress(location);
            baiduException.setGmtCreate(new Date());
            baiduException.setExceptionMsg(e.getMessage());
            iBaiduExceptionService.insert(baiduException);
        }

        return strAddress;
    }

    /**
     *
     * @param coords 需转换的源坐标，多组坐标以“；”分隔（经度，纬度）
     * @param from   源坐标类型： 1：GPS设备获取的角度坐标，wgs84坐标; 2：GPS获取的米制坐标、sogou地图所用坐标;
     *               3：google地图、soso地图、aliyun地图、mapabc地图和amap地图所用坐标，国测局（gcj02）坐标;
     *               4：3中列表地图坐标对应的米制坐标; 5：百度地图采用的经纬度坐标; 6：百度地图采用的米制坐标; 7：mapbar地图坐标;
     *               8：51地图坐标
     * @param to     目标坐标类型： 3：国测局（gcj02）坐标; 4：3中对应的米制坐标; 5：bd09ll(百度经纬度坐标);
     *               6：bd09mc(百度米制经纬度坐标)
     * @return
     * @throws BaseException
     *
     */
    public static List<Map<String, Double>> geoConv(String coords, int from, int to) throws BaseException {
        List<Map<String, Double>> lists = Lists.newArrayList();
        String url = String.format(baiDuMapGeoConfig.getGeoconvUrl(), coords, from, to, baiDuMapCoreConfig.getApiKey());
        try {
            String strReturn = HttpClientUtil.get(url, null, socketTimeout);
            JSONObject jsonObject = JSON.parseObject(strReturn);
            int status = jsonObject.getInteger("status");
            String msg = BaiDuMapStatusEnum.getTest(status);
            if (status == 0) {
                JSONArray jsonArray = jsonObject.getJSONArray("result");
                if (!CollectionUtils.isEmpty(jsonArray)) {
                    jsonArray.forEach(object1 -> {
                        JSONObject jsonObject1 = (JSONObject) object1;
                        Map<String, Double> map = Maps.newHashMap();
                        map.put("lng", jsonObject1.getDouble("x"));
                        map.put("lat", jsonObject1.getDouble("y"));
                        lists.add(map);
                    });
                }
            } else {
                throw new BaseException(msg);
            }
        } catch (Exception e) {
            logger.error("geoConv error: {}", e.getMessage());
            BaiduException baiduException = new BaiduException();
            baiduException.setAddress(coords);
            baiduException.setGmtCreate(new Date());
            baiduException.setExceptionMsg(e.getMessage());
            iBaiduExceptionService.insert(baiduException);
        }

        return lists;
    }
}
